package com.katze.boot.system.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.katze.boot.common.domain.GlobalConstant;
import com.katze.boot.plugins.redis.service.CacheService;
import com.katze.boot.system.dao.MenuMapper;
import com.katze.boot.system.domain.Menu;
import com.katze.boot.system.domain.MenuNode;
import com.katze.boot.system.service.MenuService;
import com.katze.common.tree.Node;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.*;
import java.util.stream.Collectors;

@Service("menuService")
@Transactional(propagation = Propagation.SUPPORTS, readOnly = true, rollbackFor = Exception.class)
public class MenuServiceImpl extends ServiceImpl<MenuMapper, Menu> implements MenuService {

    @Resource
    private CacheService cacheService;

    /**
     * 通过用户名构建 Vue路由
     *
     * @param username 用户名
     * @return 路由集合
     */
    public Map<String, Object> getUserRouters(String username) {
        List<MenuNode> menus = this.baseMapper.findUserMenuRouters(username);
        Map<Boolean, List<MenuNode>> data = menus.stream().collect(Collectors.groupingBy(item-> Menu.TYPE_BUTTON.equals(item.getType())));
        Map<String, Object> map = new HashMap<>();
        if (data.containsKey(false)){
            map.put("menus", Node.build(data.get(false)));
        }
        if (data.containsKey(true)){
            map.put("buttons", data.get(true).stream().collect(Collectors.toMap(MenuNode::getPerms, o-> o)));
        }
        return map;
    }

    @Override
    public Map<String, Object> findMenus(Menu menu) {
        Map<String, Object> result = new HashMap<>();
        try {
            LambdaQueryWrapper<Menu> queryWrapper = new LambdaQueryWrapper<>();
            boolean condition = this.findMenuCondition(queryWrapper, menu);
            List<Menu> menus = this.baseMapper.selectList(queryWrapper);

            List<String> ids = new ArrayList<>();
            List<MenuNode> nodes = menus.stream()
                    .map(MenuNode::new)
                    .peek(item -> ids.add(item.getMenuId()))
                    .collect(Collectors.toList());

            result.put("ids", ids);
            if (condition) {
                result.put("rows", nodes);
                result.put("total", menus.size());
            } else {
                result.put("rows", Node.build(nodes));
                result.put("total", 1);
            }
        } catch (NumberFormatException e) {
            log.error("查询菜单失败", e);
            result.put("rows", null);
            result.put("total", 0);
        }
        return result;
    }


    @Override
    public List<Menu> findMenuList(Menu menu) {
        LambdaQueryWrapper<Menu> queryWrapper = new LambdaQueryWrapper<>();
        findMenuCondition(queryWrapper, menu);
        queryWrapper.orderByAsc(Menu::getMenuId);
        return this.baseMapper.selectList(queryWrapper);
    }

    @Override
    @Transactional
    public void createMenu(Menu menu) {
        menu.setCreateTime(new Date());
        setMenu(menu);
        this.save(menu);
    }

    @Override
    @Transactional
    public void updateMenu(Menu menu) {
        menu.setModifyTime(new Date());
        setMenu(menu);
        baseMapper.updateById(menu);

        // 查找与这些菜单/按钮关联的用户
        List<String> users = this.baseMapper.findUsernameByMenuId(menu.getMenuId());
        // 重新将这些用户的角色和权限缓存到 Redis中
        for (String user : users) {
            this.cacheService.clearUserCache(user);
        }
    }

    @Override
    @Transactional
    public void deleteMenus(String[] menuIds) {
        this.delete(Arrays.asList(menuIds));
        for (String menuId : menuIds) {
            // 查找与这些菜单/按钮关联的用户
            List<String> users = this.baseMapper.findUsernameByMenuId(menuId);
            for (String user : users) {
                this.cacheService.clearUserCache(user);
            }
        }
    }

    @Override
    @Transactional
    public int deleteMenuByCode(String code) {
        return this.baseMapper.delete(Wrappers.lambdaUpdate(Menu.class).eq(Menu::getCode, code));
    }

    private void setMenu(Menu menu) {
        if (StringUtils.isBlank(menu.getParentId()))
            menu.setParentId("0");
        if (Menu.TYPE_BUTTON.equals(menu.getType())) {
            menu.setPath(null);
            menu.setComponent(null);
        }
    }

    private boolean findMenuCondition(LambdaQueryWrapper<Menu> queryWrapper, Menu menu) {
        boolean condition = false;

        if (StringUtils.isNotBlank(menu.getType())) {
            if (menu.getType().contains(",")) {
                Object[] o = menu.getType().split(",");
                queryWrapper.in(Menu::getType, o);
            } else {
                queryWrapper.eq(Menu::getType, menu.getType());
            }
            condition = GlobalConstant.TYPE_BUTTON.equals(menu.getType());
        }

        if (StringUtils.isNotBlank(menu.getTitle())) {
            queryWrapper.like(Menu::getTitle, menu.getTitle());
            condition = true;
        }

        if (StringUtils.isNotBlank(menu.getParentId())) {
            queryWrapper.eq(Menu::getParentId, menu.getParentId());
            condition = true;
        }

        if (StringUtils.isNotBlank(menu.getCreateTimeFrom()) && StringUtils.isNotBlank(menu.getCreateTimeTo())) {
            queryWrapper
                    .ge(Menu::getCreateTime, menu.getCreateTimeFrom())
                    .le(Menu::getCreateTime, menu.getCreateTimeTo());
            condition = true;
        }
        return condition;
    }


    private void delete(List<String> menuIds) {
        removeByIds(menuIds);

        LambdaQueryWrapper<Menu> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.in(Menu::getParentId, menuIds);
        List<Menu> menus = baseMapper.selectList(queryWrapper);
        if (CollectionUtils.isNotEmpty(menus)) {
            List<String> menuIdList = new ArrayList<>();
            menus.forEach(m -> menuIdList.add(m.getMenuId()));
            this.delete(menuIdList);
        }
    }

}